package com.darthsith.runtap.activity;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.darthsith.runtap.Constants;
import com.darthsith.runtap.R;
import com.darthsith.runtap.activity.fragment.GameFragment;
import com.darthsith.runtap.activity.fragment.MenuFragment;
import com.darthsith.runtap.activity.fragment.PreferenceFragment;
import com.google.android.gms.games.Games;
import com.google.android.gms.games.GamesActivityResultCodes;
import com.google.android.gms.games.GamesStatusCodes;
import com.google.android.gms.games.multiplayer.Invitation;
import com.google.android.gms.games.multiplayer.Multiplayer;
import com.google.android.gms.games.multiplayer.OnInvitationReceivedListener;
import com.google.android.gms.games.multiplayer.Participant;
import com.google.android.gms.games.multiplayer.realtime.RealTimeMessage;
import com.google.android.gms.games.multiplayer.realtime.RealTimeMessageReceivedListener;
import com.google.android.gms.games.multiplayer.realtime.Room;
import com.google.android.gms.games.multiplayer.realtime.RoomConfig;
import com.google.android.gms.games.multiplayer.realtime.RoomStatusUpdateListener;
import com.google.android.gms.games.multiplayer.realtime.RoomUpdateListener;
import com.google.example.games.basegameutils.BaseGameActivity;

public class MainActivity extends BaseGameActivity implements OnInvitationReceivedListener, 
																MenuFragment.OnButtonClickedListener,
																RoomUpdateListener, RealTimeMessageReceivedListener, 
																RoomStatusUpdateListener {

	private FragmentManager fragmentManager;
	private MenuFragment menuFragment;
	private PreferenceFragment preferenceFragment;
	private GameFragment gameFragment;
	
	private String invitationId;
	private String mRoomId;
	private ArrayList<Participant> participants;
	private String myId;
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //inietta il layout iniziale
        setContentView(R.layout.activity_main);
        Log.d(Constants.TAG, "On activity created");
        //istanzia il fragmentManager e i fragment dell'applicazione
        fragmentManager=getFragmentManager();
        menuFragment=new MenuFragment();
        preferenceFragment=new PreferenceFragment();
        gameFragment=new GameFragment();
        gameFragment.setArguments(new Bundle());
        //inietta nel layout il fragment relativo al menu
        fragmentManager.beginTransaction().add(R.id.main_layout, menuFragment).commit();
        enableDebugLog(true);
    }
    
    @Override
    protected void onResume() {
    	super.onResume();
    }
    
    @Override
	protected void onDestroy() {
		super.onDestroy();
		//se c'è una partita in corso, lascia la Room
		if (mRoomId!=null && getApiClient().isConnected())
			Games.RealTimeMultiplayer.leave(getApiClient(), this, mRoomId);
	}
    
    @Override
    public void onBackPressed() {
    	//ridefinisco l'onBackPressed per evitare di uscire dall'app555
    	switchView(menuFragment);
    }
    
    @Override
	public void onButtonClicked(int id) {
		if (id == R.id.sign_in_button) {
			//inizio il flusso di signIn
	        beginUserInitiatedSignIn();
	    }
		if (id == R.id.sign_out_button) {
	    	final AlertDialog alertDialog=new AlertDialog.Builder(this).create();
	    	alertDialog.setMessage(getString(R.string.signout_msg));
	    	alertDialog.setTitle(getString(R.string.signout_title));
	    	alertDialog.setButton(Dialog.BUTTON_POSITIVE, getString(R.string.ok), new DialogInterface.OnClickListener() {
				
				@Override
				public void onClick(DialogInterface dialog, int which) {
					//chiamo il signOut
					signOut();
					refreshSignInButton();
					alertDialog.cancel();
				}
			});
	    	alertDialog.setButton(Dialog.BUTTON_NEGATIVE, getString(R.string.no), new DialogInterface.OnClickListener() {
				
				@Override
				public void onClick(DialogInterface dialog, int which) {
					alertDialog.dismiss();					
				}
			});
	    	alertDialog.show();
	    }
		if (id==R.id.achievement) {
			startActivityForResult(Games.Achievements.getAchievementsIntent(getApiClient()), 0);
		}
		if (id==R.id.leaderboard) {
			startActivityForResult(Games.Leaderboards.getLeaderboardIntent(getApiClient(), getString(R.string.leaderboard_best_runner)), 0);
		}
		if (id == R.id.practice) {
			//vado al gioco in modalità singlePlayer
			gameFragment.getArguments().putBoolean("m", false);
			switchView(gameFragment);
		}
		if (id == R.id.multiplayer) {
			//se siamo connessi a una rete55
			if (checkConnection()) {
				final AlertDialog dialog=new AlertDialog.Builder(this).create();
				dialog.setTitle(getString(R.string.choose_mod));
				dialog.setButton(Dialog.BUTTON_POSITIVE, getString(R.string.online_quick), new DialogInterface.OnClickListener() {
					
					@Override
					public void onClick(DialogInterface dialog, int which) {
						dialog.cancel();
						//Sono in modalità partita rapida. Creo una Room a cui potrà accedere solo un altro Player5
						Bundle am = RoomConfig.createAutoMatchCriteria(1, 1, 0);
					    RoomConfig.Builder roomConfigBuilder = makeBasicRoomConfigBuilder();
					    roomConfigBuilder.setAutoMatchCriteria(am);
					    RoomConfig roomConfig = roomConfigBuilder.build();
					    Games.RealTimeMultiplayer.create(getApiClient(), roomConfig);
					}
				});
				dialog.setButton(Dialog.BUTTON_NEGATIVE, getString(R.string.online_friend), new DialogInterface.OnClickListener() {
					
					@Override
					public void onClick(DialogInterface dialog, int which) {
						dialog.cancel();
						//Voglio invitare un Player, quindi chiamo l'intent per la sua selezione
						Intent intent = Games.RealTimeMultiplayer.getSelectOpponentsIntent(getApiClient(), 1, 1);
				    	startActivityForResult(intent, Constants.RC_SELECT_PLAYERS);
					}
				});
				dialog.show();
			}
			else {
				Toast.makeText(this, getString(R.string.check_connection), Toast.LENGTH_SHORT).show();
			}
		}
		if (id==R.id.option) {
			switchView(preferenceFragment);
		}
		if (id==R.id.exit) {
			super.onBackPressed();
		}
	}
	
    /**
     * Metodo di utilità per swappare da un Fragment ad un altro
     * 
     * @param fragment
     */
	public void switchView(Fragment fragment) {
    	FragmentTransaction fragmentTransaction=getFragmentManager().beginTransaction();
    	fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
		fragmentTransaction.replace(R.id.main_layout, fragment);
		fragmentTransaction.addToBackStack(null);
		fragmentTransaction.commit();
    }
    
	/**
	 * Controlla se c'è una connessione attiva
	 * 
	 * @return
	 */
	private boolean checkConnection() {
		ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo i = cm.getActiveNetworkInfo();
		if (i == null)
			return false;
		if (!i.isConnected())
			return false;
		if (!i.isAvailable())
			return false;
		return true;
	}
	
	@Override
	public void onInvitationReceived(Invitation invitation) {
		invitationId=invitation.getInvitationId();
		Log.d(Constants.TAG, "onInvitationReceived: "+invitationId);
		//se c'è un invito pendente apro l'intent di accettazione
		if (invitationId!=null) {
			Intent intent = Games.Invitations.getInvitationInboxIntent(getApiClient());
			startActivityForResult(intent, Constants.RC_INVITATION_INBOX);
	    }	
	}
	
	@Override
	public void onInvitationRemoved(String invitationId) {
	}
	
	@Override
	public void onActivityResult(int request, int response, Intent data) {
		super.onActivityResult(request, response, data);
		//se accetto un invito mi unisco alla Room
		if (request == Constants.RC_INVITATION_INBOX) {
			Log.i(Constants.TAG, "Joining the Room");
			Invitation inv = data.getExtras().getParcelable(Multiplayer.EXTRA_INVITATION);
			RoomConfig.Builder roomConfigBuilder = RoomConfig.builder(this);
	        roomConfigBuilder.setInvitationIdToAccept(inv.getInvitationId())
	                .setMessageReceivedListener(this)
	                .setRoomStatusUpdateListener(this);
	        Games.RealTimeMultiplayer.join(getApiClient(), roomConfigBuilder.build());
	    }
		//una volta selezionato un player per giocare, creo la Room
		if (request == Constants.RC_SELECT_PLAYERS) {
	        if (response != Activity.RESULT_OK) {
	            return;
	        }
	        else if (response == Activity.RESULT_CANCELED) {
	        	finish();
	        }
	        ArrayList<String> invitees = data.getStringArrayListExtra(Games.EXTRA_PLAYER_IDS);
	        Bundle autoMatchCriteria = null;
	        int minAutoMatchPlayers = data.getIntExtra(Multiplayer.EXTRA_MIN_AUTOMATCH_PLAYERS, 1);
	        int maxAutoMatchPlayers = data.getIntExtra(Multiplayer.EXTRA_MAX_AUTOMATCH_PLAYERS, 1);
	        if (minAutoMatchPlayers==1 && maxAutoMatchPlayers==1) {
	            autoMatchCriteria = RoomConfig.createAutoMatchCriteria(minAutoMatchPlayers, maxAutoMatchPlayers, 0);
	        }
	        RoomConfig.Builder roomConfigBuilder = makeBasicRoomConfigBuilder();
	        roomConfigBuilder.addPlayersToInvite(invitees);
	        if (autoMatchCriteria != null) {
	            roomConfigBuilder.setAutoMatchCriteria(autoMatchCriteria);
	        }
	        RoomConfig roomConfig = roomConfigBuilder.build();
	        Games.RealTimeMultiplayer.create(getApiClient(), roomConfig);
	    }
		//la partita è stata accettata, quindi invio un byte per comunicare che sono pronto, e lancio il gioco
		if (request == Constants.RC_WAITING_ROOM) {
	        if (response == Activity.RESULT_OK) {
	        	byte[] msg=new byte[1];
	        	msg[0]='S';
	        	for (Participant p : participants) {
	        		if (!p.getParticipantId().equals(myId)) {
	        			Games.RealTimeMultiplayer.sendReliableMessage(getApiClient(), null, msg, mRoomId, p.getParticipantId());
	        		}
	            }
				gameFragment.getArguments().putBoolean("m", true);
	        	switchView(gameFragment);
	        }
	        else if (response == Activity.RESULT_CANCELED) {
	        	if (mRoomId!=null) {
	        		Games.RealTimeMultiplayer.leave(getApiClient(), this, mRoomId);
	        		finish();
	        	}
	        }
	        else if (response == GamesActivityResultCodes.RESULT_LEFT_ROOM) {
	        	if (mRoomId!=null)
	        		Games.RealTimeMultiplayer.leave(getApiClient(), this, mRoomId);
	            finish();
	        }
	    }
	}
	
	@Override
	public void onRealTimeMessageReceived(RealTimeMessage message) {
		byte[] msg=message.getMessageData();
		//se ricevo S vuol dire che il player avversario è pronto, e lancio il gioco
		if (msg[0]=='S') {
			gameFragment.getArguments().putBoolean("m", true);
			switchView(gameFragment);
		}
		//se ricevo altro la partita è finita, e il payload è il numero di passi
		else {
			//estraggo il numero passi dal buffer di byte
			byte[] buf=message.getMessageData();
			ByteBuffer byteBuffer=ByteBuffer.wrap(buf);
			int stepNumber=byteBuffer.getInt();
			buildAlert("Hai percorso "+gameFragment.getStepNumber()+" passi. Il tuo avversario ne ha fatti "+stepNumber+".");
		}
	}

	/**
	 * Metodo di utilità per mandare messaggi al player avversario
	 * 
	 * @param buf
	 */
	public void sendMsg(byte[] buf) {
		for (Participant p : participants) {
    		if (!p.getParticipantId().equals(myId)) {
    			Games.RealTimeMultiplayer.sendReliableMessage(getApiClient(), null, buf, mRoomId, p.getParticipantId());
    		}
        }
	}
	
	@Override
	public void onSignInSucceeded() {
		Log.d(Constants.TAG, "onSignInSucceeded");
		refreshSignInButton();
		Games.Invitations.registerInvitationListener(getApiClient(), this);
		//se c'è un invito pendente lo accetto
		if (getInvitationId() != null) {
			RoomConfig.Builder roomConfigBuilder = RoomConfig.builder(this);
	        roomConfigBuilder.setInvitationIdToAccept(getInvitationId())
	                .setMessageReceivedListener(this)
	                .setRoomStatusUpdateListener(this);
	        Games.RealTimeMultiplayer.join(getApiClient(), roomConfigBuilder.build());
            return;
        }
	}
	
	@Override
	public void onSignInFailed() {
		Log.d(Constants.TAG, "onSignInFailed");
		refreshSignInButton();
	    Toast.makeText(this, getString(R.string.common_google_play_services_network_error_text), Toast.LENGTH_SHORT).show();
	}

	private RoomConfig.Builder makeBasicRoomConfigBuilder() {
	    return RoomConfig.builder(this)
	            .setMessageReceivedListener(this)
	            .setRoomStatusUpdateListener(this);
	}
	
	@Override
	public void onP2PConnected(String participantId) {
	}

	@Override
	public void onP2PDisconnected(String participantId) {
		buildAlert(getString(R.string.peer_left));
	}

	@Override
	public void onJoinedRoom(int statusCode, Room room) {
		if (statusCode != GamesStatusCodes.STATUS_OK) {
	        Intent i = Games.RealTimeMultiplayer.getWaitingRoomIntent(getApiClient(), room, 2);
		    startActivityForResult(i, Constants.RC_WAITING_ROOM);	
		}		
	}

	@Override
	public void onLeftRoom(int statusCode, String roomId) {
		switchView(new MenuFragment());
	}

	@Override
	public void onRoomConnected(int statusCode, Room room) {
		if (statusCode != GamesStatusCodes.STATUS_OK) {
	        participants=room.getParticipants();
	        Log.i(Constants.TAG, "PLAYERS: "+participants.toString());
	    }		
	}

	@Override
	public void onRoomCreated(int statusCode, Room room) {
		Intent i = Games.RealTimeMultiplayer.getWaitingRoomIntent(getApiClient(), room, 2);
	    startActivityForResult(i, Constants.RC_WAITING_ROOM);
	}

	@Override
	public void onConnectedToRoom(Room room) {
		mRoomId=room.getRoomId();
		participants=room.getParticipants();
		myId = room.getParticipantId(Games.Players.getCurrentPlayerId(getApiClient()));
	}

	@Override
	public void onDisconnectedFromRoom(Room room) {
		mRoomId=null;
	}

	@Override
	public void onPeerDeclined(Room room, List<String> arg1) {
		mRoomId=room.getRoomId();
		participants=room.getParticipants();
		myId = room.getParticipantId(Games.Players.getCurrentPlayerId(getApiClient()));
	}

	@Override
	public void onPeerInvitedToRoom(Room room, List<String> arg1) {
		mRoomId=room.getRoomId();
		participants=room.getParticipants();
		myId = room.getParticipantId(Games.Players.getCurrentPlayerId(getApiClient()));
	}

	@Override
	public void onPeerJoined(Room room, List<String> arg1) {
		mRoomId=room.getRoomId();
		participants=room.getParticipants();
		myId = room.getParticipantId(Games.Players.getCurrentPlayerId(getApiClient()));
	}

	@Override
	public void onPeerLeft(Room room, List<String> arg1) {
		mRoomId=room.getRoomId();
		participants=room.getParticipants();
		myId = room.getParticipantId(Games.Players.getCurrentPlayerId(getApiClient()));
		buildAlert(getString(R.string.peer_left));
	}

	@Override
	public void onPeersConnected(Room room, List<String> arg1) {
		mRoomId=room.getRoomId();
		participants=room.getParticipants();
		myId = room.getParticipantId(Games.Players.getCurrentPlayerId(getApiClient()));
	}

	@Override
	public void onPeersDisconnected(Room room, List<String> arg1) {
		mRoomId=room.getRoomId();
		participants=room.getParticipants();
		myId = room.getParticipantId(Games.Players.getCurrentPlayerId(getApiClient()));
//		buildAlert(getString(R.string.peer_left));
	}

	@Override
	public void onRoomAutoMatching(Room room) {
		mRoomId=room.getRoomId();
		participants=room.getParticipants();
		myId = room.getParticipantId(Games.Players.getCurrentPlayerId(getApiClient()));
	}

	@Override
	public void onRoomConnecting(Room room) {
		mRoomId=room.getRoomId();
		participants=room.getParticipants();
		myId = room.getParticipantId(Games.Players.getCurrentPlayerId(getApiClient()));
	}
	
	/**
	 * Sblocca trofeo
	 * 
	 * @param achieveId
	 */
	public void unlockAchieve(String achieveId) {
		Games.Achievements.unlock(getApiClient(), achieveId);
	}
	
	/**
	 * Incrementa trofeo
	 * 
	 * @param achieveId
	 */
	public void incrementAchieve(String achieveId) {
		Games.Achievements.increment(getApiClient(), achieveId, 1);
	}
	
	/**
	 * Pubblica punteggio
	 * 
	 * @param score
	 */
	public void uploadScore(long score) {
		Log.i(Constants.TAG, "Uploading score: "+score);
		Games.Leaderboards.submitScore(getApiClient(), getString(R.string.leaderboard_best_runner), score);
	}
	
	public void buildAlert(String message) {
		AlertDialog alertDialog=new AlertDialog.Builder(this).create();
		alertDialog.setButton(Dialog.BUTTON_POSITIVE, getString(R.string.ok), new DialogInterface.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				switchView(new MenuFragment());
			}
		});
		alertDialog.setMessage(message);
		alertDialog.setCancelable(false);
		alertDialog.show();
	}
	
	public MenuFragment getMenuFragment() {
		return menuFragment;
	}
	
	public PreferenceFragment getPreferenceFragment() {
		return preferenceFragment;
	}
	
	public GameFragment getGameFragment() {
		return gameFragment;
	}
	
	public void refreshSignInButton() {
		if (getApiClient().isConnected()) {
			findViewById(R.id.sign_in_button).setVisibility(View.GONE);
			findViewById(R.id.sign_out_button).setVisibility(View.VISIBLE);
			findViewById(R.id.achievement).setVisibility(View.VISIBLE);
			findViewById(R.id.leaderboard).setVisibility(View.VISIBLE);
		}
		else {
			findViewById(R.id.sign_in_button).setVisibility(View.VISIBLE);
		    findViewById(R.id.sign_out_button).setVisibility(View.GONE);
		    findViewById(R.id.achievement).setVisibility(View.GONE);
		    findViewById(R.id.leaderboard).setVisibility(View.GONE);
		}
	}
	
}
